package cn.com.dcsgo;

/**
 * JaroWinkler
 *
 * @author Dcsgo
 * @since 2025/4/20 上午6:31
 */

public class JaroWinkler {

    private static final double THRESHOLD = 0.1; // Jaro-Winkler 相似度阈值

    public static double jaroWinkler(String s1, String s2) {
        // 计算 Jaro 距离
        double jaroDistance = jaroDistance(s1, s2);

        // 如果 Jaro 距离高于阈值，计算 Winkler 距离
        if (jaroDistance > THRESHOLD) {
            int l = commonPrefixLength(s1, s2);
            return jaroDistance + (l * 0.1 * (1 - jaroDistance));
        }

        return jaroDistance;
    }

    private static double jaroDistance(String s1, String s2) {
        int len1 = s1.length();
        int len2 = s2.length();

        if (len1 == 0 && len2 == 0) {
            return 1.0;
        }

        int matchDistance = Math.max(len1, len2) / 2 - 1;

        boolean[] s1Matches = new boolean[len1];
        boolean[] s2Matches = new boolean[len2];

        int matches = 0;
        for (int i = 0; i < len1; i++) {
            for (int j = Math.max(0, i - matchDistance); j <= Math.min(len2 - 1, i + matchDistance); j++) {
                if (s2Matches[j]) {
                    continue;
                }
                if (s1.charAt(i) != s2.charAt(j)) {
                    continue;
                }
                s1Matches[i] = true;
                s2Matches[j] = true;
                matches++;
                break;
            }
        }

        // 如果没有匹配
        if (matches == 0) {
            return 0.0;
        }

        // 计算转置
        int t = 0;
        int point = 0;
        for (int i = 0; i < len1; i++) {
            if (!s1Matches[i]) {
                continue;
            }
            while (!s2Matches[point]) {
                point++;
            }
            if (s1.charAt(i) != s2.charAt(point)) {
                t++;
            }
            point++;
        }

        return ((matches / (double) len1) + (matches / (double) len2) + ((matches - t) / (double) matches)) / 3.0;
    }

    private static int commonPrefixLength(String s1, String s2) {
        int maxLength = Math.min(s1.length(), s2.length());
        int length = 0;

        for (int i = 0; i < maxLength && s1.charAt(i) == s2.charAt(i); i++) {
            length++;
        }

        return length;
    }
}
